﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using BrightIdeasSoftware;
using TeachMe.Application.Configuration;
using TeachMe.Application.Controls;
using TeachMe.Application.Data;
using TeachMe.Application.Events;
using TeachMe.Application.Exceptions;
using TeachMe.Application.Properties;

namespace TeachMe.Application.Forms
{
    public partial class EntriesEditorForm : PseudoModalForm
    {
        private class CurrentDictionaryWrapper
        {
            public event EventHandler<EventArgs> Changed;

            private Dictionary _dictionary;

            public Dictionary Dictionary
            {
                get
                {
                    return _dictionary;
                }
                set
                {
                    if (_dictionary != null)
                    {
                        _dictionary.EntriesModified -= OnDictionaryChanged;
                    }
                    _dictionary = value;
                    if (_dictionary != null)
                    {
                        _dictionary.EntriesModified += OnDictionaryChanged;
                    }
                }
            }

            void OnDictionaryChanged(object sender, DictionaryEntriesEventArgs e)
            {
                EventHandler<EventArgs> handler = Changed;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }

        private readonly CurrentDictionaryWrapper _currentDictionary;
        private readonly Root _root;
        private readonly List<EditedEntryDescriptor> _entries;
        private int _skipChangedDictionaryCounter;
        private bool _savingChanges;
        /// <summary>
        /// We cannot use dicts from root because there may be some deleted dicts (when opened from settings dlg)
        /// which would prevent us from using same file name for dict.
        /// </summary>
        private readonly List<Dictionary> _dicts;

        /// <summary>
        /// Dict created in current session.
        /// </summary>
        public Dictionary CreatedDictionary { get; private set; }

        public EntriesEditorForm(Root root, List<Dictionary> dicts)
        {
            _root = root;
            _root.MachineSettings.CheckOut();

            _entries = new List<EditedEntryDescriptor>();
            _dicts = dicts;

            _currentDictionary = new CurrentDictionaryWrapper();
            _currentDictionary.Changed += CurrentDictionaryChanged;

            InitializeComponent();
            InitializeEntriesTable();
            InitializePriorityChanger();

            cbFrom.Fill();
            cbTo.Fill();

            FillDictionaries();

            UpdateButtons();

            lvEntries.SetObjects(GetEditorContent());

            WindowProportions.Lock(this, _root.MachineSettings);
            ListViewColumnsProportions.Lock(this, lvEntries, _root.MachineSettings);

            tcMain.SelectedTab = tpMetadata;
        }

        public EntriesEditorForm(Root root, List<Dictionary> dicts, Dictionary defaultSelection)
        {
            _root = root;
            _root.MachineSettings.CheckOut();

            _entries = new List<EditedEntryDescriptor>();
            _dicts = dicts;

            _currentDictionary = new CurrentDictionaryWrapper();
            _currentDictionary.Dictionary = defaultSelection;
            _currentDictionary.Changed += CurrentDictionaryChanged;

            InitializeComponent();
            InitializeEntriesTable();

            cbFrom.Fill();
            cbTo.Fill();
            
            FillEntries();
            FillMetadata();
            FillDictionaries();
            InitializePriorityChanger();

            cbDictionaries.SelectedItem = defaultSelection;

            UpdateButtons();

            WindowProportions.Lock(this, _root.MachineSettings);
            ListViewColumnsProportions.Lock(this, lvEntries, _root.MachineSettings);

            tcMain.SelectedTab = tpEntries;
        }

        private void InitializePriorityChanger()
        {
            miPriority.Text = colPriority.Text;

            miPriority.DropDownItems.Add(Resources.VeryLowPriority, Resources.priority_very_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.VeryLow));
            miPriority.DropDownItems.Add(Resources.LowPriority, Resources.priority_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Low));
            miPriority.DropDownItems.Add(Resources.NormalPriority, Resources.priority_normal).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Normal));
            miPriority.DropDownItems.Add(Resources.HighPriority, Resources.priority_high).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.High));

            tsbPriority.DropDownItems.Add(Resources.VeryLowPriority, Resources.priority_very_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.VeryLow));
            tsbPriority.DropDownItems.Add(Resources.LowPriority, Resources.priority_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Low));
            tsbPriority.DropDownItems.Add(Resources.NormalPriority, Resources.priority_normal).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Normal));
            tsbPriority.DropDownItems.Add(Resources.HighPriority, Resources.priority_high).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.High));
        }

        private void ChangePriorityForSelectedItems(Priority priority)
        {
            foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
            {
                descriptor.Priority = priority;
            }

            lvEntries.RefreshObjects(lvEntries.SelectedObjects);
            UpdateButtons();
        }

        void CurrentDictionaryChanged(object sender, EventArgs e)
        {
            if (_savingChanges)
            {
                return;
            }

            if (MessageBox.Show(Resources.DictionaryChangedYouWantReload, Resources.MessageBoxCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Revert();
            }
        }

        private void InitializeEntriesTable()
        {
            ObjectListView.EditorRegistry.Register(typeof(Priority), typeof(PriorityComboBox));

            // let height be like combo
            lvEntries.RowHeight = new ComboBox().Height;

            colPriority.AspectType = typeof(Priority);

            colPriority.AspectGetter += delegate(object rowObject)
                                            {
                                                EditedEntryDescriptor descriptor = rowObject as EditedEntryDescriptor;
                                                if (descriptor == null)
                                                {
                                                    throw new InvalidOperationException();
                                                }
                                                switch (descriptor.Priority)
                                                {
                                                    case Priority.VeryLow:
                                                        return Resources.VeryLowPriority;
                                                    case Priority.Low:
                                                        return Resources.LowPriority;
                                                    case Priority.Normal:
                                                        return Resources.NormalPriority;
                                                    case Priority.High:
                                                        return Resources.HighPriority;
                                                    default:
                                                        throw new ArgumentOutOfRangeException();
                                                }
                                            };

            colPriority.AspectPutter += delegate
                                            {
                                                // ignored
                                            };
        }

        private void FillMetadata()
        {
            tbFileName.ReadOnly = true;
            bBrowseForFileName.Enabled = false;

            tbFileName.Text = _currentDictionary.Dictionary.UserData.Path;
            tbFileName.Modified = false;
            tbName.Text = _currentDictionary.Dictionary.Entries.Name;
            tbName.Modified = false;
            cbFrom.Code = _currentDictionary.Dictionary.Entries.From;
            cbFrom.Modified = false;
            cbTo.Code = _currentDictionary.Dictionary.Entries.To;
            cbTo.Modified = false;
            tbDescription.Text = _currentDictionary.Dictionary.Entries.Description;
            tbDescription.Modified = false;
        }

        private void FillEntries()
        {
            _entries.Clear();

            foreach (Entry entry in _currentDictionary.Dictionary.Entries.Items.Values)
            {
                _entries.Add(EditedEntryDescriptor.Create(entry, _currentDictionary.Dictionary.GetEntryPriority(entry.Id)));
            }
            
            lvEntries.SetObjects(GetEditorContent());
        }

        public IEnumerable<EditedEntryDescriptor> GetEditorContent()
        {
            foreach (EditedEntryDescriptor descriptor in _entries)
            {
                if (descriptor.State == EditedItemState.Deleted)
                {
                    continue;
                }

                yield return descriptor;
            }
        }

        private void FillDictionaries()
        {
            object currentSelection = cbDictionaries.SelectedItem ?? CreatedDictionary;

            cbDictionaries.Items.Clear();

            foreach (Dictionary dictionary in _dicts)
            {
                cbDictionaries.Items.Add(dictionary);
            }

            try
            {
                _skipChangedDictionaryCounter++;

                cbDictionaries.SelectedItem = currentSelection;
            }
            finally
            {
                _skipChangedDictionaryCounter--;
            }
        }

        private void OnCellEditStarting(object sender, CellEditEventArgs e)
        {
            EditedEntryDescriptor editedEntryDescriptor = e.RowObject as EditedEntryDescriptor;
            if (editedEntryDescriptor == null)
            {
                throw new InvalidOperationException();
            }

            if (e.Column == colPriority)
            {
                PriorityComboBox priorityComboBox = e.Control as PriorityComboBox;
                if (priorityComboBox == null)
                {
                    throw new InvalidOperationException();
                }

                priorityComboBox.FillPriorities();
                priorityComboBox.Priority = editedEntryDescriptor.Priority;
            }
        }

        private void OnCellEditFinishing(object sender, CellEditEventArgs e)
        {
            EditedEntryDescriptor editedEntryDescriptor = e.RowObject as EditedEntryDescriptor;
            if (editedEntryDescriptor == null)
            {
                throw new InvalidOperationException();
            }

            if (e.Column == colPriority)
            {
                PriorityComboBox priorityComboBox = e.Control as PriorityComboBox;
                if (priorityComboBox == null)
                {
                    throw new InvalidOperationException();
                }

                editedEntryDescriptor.Priority = priorityComboBox.Priority;
            }

            if (!tsbSave.Enabled)
            {
                tsbSave.Enabled = IsModified();
            }
        }

        private void OnAddEntryClick(object sender, EventArgs e)
        {
            EditedEntryDescriptor descriptor = EditedEntryDescriptor.CreateNew();
            
            lvEntries.AddObject(descriptor);
            lvEntries.SelectedObject = descriptor;
            
            _entries.Add(descriptor);

            UpdateButtons();
        }

        private void OnRemoveEntriesClick(object sender, EventArgs e)
        {
            RemoveSelectedEntries();
        }

        private void RemoveSelectedEntries()
        {
            List<EditedEntryDescriptor> deleteCompletely = new List<EditedEntryDescriptor>();
            
            foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
            {
                if (descriptor.State == EditedItemState.New)
                {
                    deleteCompletely.Add(descriptor);
                }
                else
                {
                    descriptor.State = EditedItemState.Deleted;
                }
            }

            foreach (EditedEntryDescriptor descriptor in deleteCompletely)
            {
                _entries.Remove(descriptor);
            }

            lvEntries.RemoveObjects(lvEntries.SelectedObjects);

            UpdateButtons();
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (IsModified())
            {
                /*
                if (DialogResult == DialogResult.Cancel)
                {
                    DialogResult result = MessageBox.Show(Resources.SaveChangedDictionary, Resources.MessageBoxCaption, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                    if (result != DialogResult.Yes)
                    {
                        cbDictionaries.SelectedItem = _currentDictionary.Dictionary;
                        if (result == DialogResult.Cancel)
                        {
                            e.Cancel = true;
                        }
                        return;
                    }
                }
                */
                
                if (!SaveChanges())
                {
                    e.Cancel = true;
                }
            }
        }

        private bool SaveChanges()
        {
            string path = tbFileName.Text.Trim();

            try
            {
                _savingChanges = true;

                if (!Utilities.IsNotEmpty(tbFileName.Text, cbFrom.Code, cbTo.Code, tbName.Text))
                {
                    MessageBox.Show(Resources.IncompleteDictionaryMetadata, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Stop);

                    return false;
                }

                bool creatingNewDictionary = _currentDictionary.Dictionary == null;

                if (creatingNewDictionary)
                {
                    if (File.Exists(path))
                    {
                        DialogResult answer = MessageBox.Show(String.Format(Resources.FileWillBeOverwriten, path), Resources.MessageBoxCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Question);

                        switch (answer)
                        {
                            case DialogResult.No:
                                return false;
                            case DialogResult.Yes:
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }

                    foreach (Dictionary dict in _dicts)
                    {
                        if (Utilities.Equals(dict.UserData.Path, path))
                        {
                            MessageBox.Show(String.Format(Resources.DictionaryAlreadyUsed, path), Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Stop);

                            return false;
                        }
                    }

                    try
                    {
                        using (File.Create(path))
                        {

                        }
                    }
                    catch (Exception e)
                    {
                        Logging.Error(GetType(), e, "File '{0}' cannot be created.", path);

                        MessageBox.Show(Resources.DictionaryCreateError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Stop);

                        return false;
                    }

                    if (_currentDictionary == null)
                    {
                        throw new InvalidOperationException();
                    }

                    try
                    {
                        _currentDictionary.Dictionary = _root.CreateNewDictionary(path, tbName.Text, tbDescription.Text, cbFrom.Code, cbTo.Code);
                    }
                    catch(DictionaryException exception)
                    {
                        Logging.Error(GetType(), exception, "Dictionary could not be created.");

                        MessageBox.Show(Resources.DictionaryCreateError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                        return false;
                    }

                    _dicts.Add(_currentDictionary.Dictionary);
                    CreatedDictionary = _currentDictionary.Dictionary;
                }
                else
                {
                    _currentDictionary.Dictionary.UpdateMetadata(tbName.Text, tbDescription.Text, cbFrom.Code, cbTo.Code);
                }

                List<EditedEntryDescriptor> editedEntries = new List<EditedEntryDescriptor>();

                foreach (EditedEntryDescriptor entryWrapper in _entries)
                {
                    if (entryWrapper.IsModified() || entryWrapper.State != EditedItemState.Normal)
                    {
                        editedEntries.Add(entryWrapper);
                    }
                }

                _currentDictionary.Dictionary.UpdateEntries(editedEntries);

                // name may have changed or new dictionary may have been created and we have to add it into list

                FillDictionaries();

                return true;
            }
            catch (DictionaryException exception)
            {
                Logging.Error(GetType(), exception, "Dictionary could not be saved.");

                MessageBox.Show(Resources.DictionarySaveError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                
                return false;
            }
            finally
            {
                _savingChanges = false;
            }
        }

        private bool IsModified()
        {
            // new dictionary is always modified

            if (_currentDictionary.Dictionary == null)
            {
                return true;
            }

            // entries

            foreach (EditedEntryDescriptor entryWrapper in _entries)
            {
                if (entryWrapper.IsModified() || entryWrapper.State != EditedItemState.Normal)
                {
                    return true;
                }
            }

            // metadata

            return tbFileName.Modified || tbName.Modified || cbFrom.Modified || cbTo.Modified || tbDescription.Modified;
        }

        private void OnSelectedDictionaryChanged(object sender, EventArgs e)
        {
            if (_skipChangedDictionaryCounter > 0 || _currentDictionary.Dictionary == cbDictionaries.SelectedItem)
            {
                return;
            }

            if (IsModified())
            {
                try
                {
                    _skipChangedDictionaryCounter++;

                    DialogResult result = MessageBox.Show(Resources.SaveChangedDictionary, Resources.MessageBoxCaption, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                    if (result == DialogResult.Cancel)
                    {
                        cbDictionaries.SelectedItem = _currentDictionary.Dictionary;
                        return;
                    }
                    else if (result == DialogResult.Yes)
                    {
                        if (!SaveChanges())
                        {
                            cbDictionaries.SelectedItem = _currentDictionary.Dictionary;
                            return;    
                        }
                    }
                }
                finally
                {
                    _skipChangedDictionaryCounter--;
                }
            }

            _currentDictionary.Dictionary = cbDictionaries.SelectedItem as Dictionary;

            FillEntries();
            FillMetadata();
            UpdateButtons();
        }

        private void OnSaveClick(object sender, EventArgs e)
        {
            if (SaveChanges())
            {
                FillEntries();
                FillMetadata();
                UpdateButtons();
            }
        }

        private void OnEntriesSelectionChanged(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void UpdateMainToolbarButtons()
        {
            bool modified = IsModified();
            
            tsbSave.Enabled = modified;
            tsbRevert.Enabled = modified && _currentDictionary != null && _currentDictionary.Dictionary != null;
        }

        private void UdpateEntriesToolbar()
        {
            tsbRemoveEntries.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbCopy.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbCut.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbDisplayEntry.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbExcludeFromImport.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbEditEntry.Enabled = lvEntries.SelectedObjects.Count > 0;
            tsbSelectAll.Enabled = lvEntries.Items.Count > 0;
            tsbPriority.Enabled = lvEntries.SelectedObjects.Count > 0;
        }

        private bool CanSelectedEntriesBeDisplayed()
        {
            if (_currentDictionary.Dictionary == null)
            {
                return false;
            }

            foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
            {
                if (descriptor.State != EditedItemState.Normal || descriptor.IsModified())
                {
                    return false;
                }
            }

            return true;
        }

        private void OnEntriesContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            miRemoveEntries.Enabled = lvEntries.SelectedObjects.Count > 0;
            miCopyEntries.Enabled = lvEntries.SelectedObjects.Count > 0;
            miCutEntries.Enabled = lvEntries.SelectedObjects.Count > 0;
            miDisplayEntries.Enabled = lvEntries.SelectedObjects.Count > 0;
            miExcludeFromImport.Enabled = lvEntries.SelectedObjects.Count > 0;
            miTranslate.Enabled = lvEntries.SelectedObjects.Count > 0;
            miPasteEntries.Enabled = true;
            miPriority.Enabled = lvEntries.SelectedObjects.Count > 0;
        }

        private void OnValuePut(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void OnRevertClick(object sender, EventArgs e)
        {
            Revert();
        }

        private void Revert()
        {
            FillEntries();
            FillMetadata();
            UpdateButtons();
        }

        private void UpdateButtons()
        {
            UpdateMainToolbarButtons();
            UdpateEntriesToolbar();
        }

        private void OnMetadataChanged(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void OnDictionaryPathBrowse(object sender, EventArgs e)
        {
            if (Directory.Exists(_root.MachineSettings.LastBrowseDirectory))
            {
                sfdDictionary.InitialDirectory = _root.MachineSettings.LastBrowseDirectory;
            }

            if (sfdDictionary.ShowDialog() == DialogResult.OK)
            {
                tbFileName.Text = sfdDictionary.FileName;

                if (Directory.Exists(Path.GetDirectoryName(sfdDictionary.FileName)))
                {
                    using (_root.MachineSettings.Edit())
                    {
                        _root.MachineSettings.LastBrowseDirectory = Path.GetDirectoryName(sfdDictionary.FileName);
                    }
                }
            }
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            _currentDictionary.Dictionary = null;

            _root.MachineSettings.CheckIn();
        }

        private void OnDisplayEntriesClick(object sender, EventArgs e)
        {
            if (!CanSelectedEntriesBeDisplayed())
            {
                MessageBox.Show(Resources.EntriesMustBeSavedBeforeDisplaying, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
                {
                    _root.DisplayEntry(_currentDictionary.Dictionary, descriptor.Id);
                }
            }
        }

        private ClipboardEntries CreateDataObjectFromSelectedEntries()
        {
            ClipboardEntries dataObject = new ClipboardEntries();

            if (_currentDictionary.Dictionary != null)
            {
                dataObject.From = _currentDictionary.Dictionary.Entries.From;
                dataObject.To = _currentDictionary.Dictionary.Entries.To;
            }

            dataObject.Items = new List<ClipboardEntries.ClipboardEntry>();

            foreach (EditedEntryDescriptor descriptor in lvEntries.GetSelectedObjects())
            {
                ClipboardEntries.ClipboardEntry clipboardEntry = new ClipboardEntries.ClipboardEntry();

                clipboardEntry.From = descriptor.From;
                clipboardEntry.To = descriptor.To;
                clipboardEntry.Example = descriptor.Example;
                clipboardEntry.Priority = descriptor.Priority;

                dataObject.Items.Add(clipboardEntry);
            }

            return dataObject;
        }

        private void OnCopyEntriesClick(object sender, EventArgs e)
        {
            CopySelectedEntries();
        }

        private void CopySelectedEntries()
        {
            ClipboardEntries clipboardEntries = CreateDataObjectFromSelectedEntries();
            clipboardEntries.Copy();
        }

        private void OnPasteEntriesClick(object sender, EventArgs e)
        {
            EditedEntryDescriptor descriptor = null;

            if (Clipboard.ContainsData(ClipboardEntries.CustomFormatName))
            {
                ClipboardEntries clipboardEntries = (ClipboardEntries)Clipboard.GetData(ClipboardEntries.CustomFormatName);

                if (clipboardEntries != null && !clipboardEntries.IsEmpty)
                {
                    bool swap = false;
                    string currentFrom = cbFrom.Code;
                    string currentTo = cbTo.Code;

                    if (!Utilities.Equals(currentFrom, clipboardEntries.From) && 
                        (Utilities.Equals(currentFrom, clipboardEntries.To) || Utilities.Equals(currentTo, clipboardEntries.From)))
                    {
                        swap = true;
                    }

                    foreach (ClipboardEntries.ClipboardEntry clipboardEntry in clipboardEntries.Items)
                    {
                        descriptor = EditedEntryDescriptor.CreateNew();
                        descriptor.From = swap ?  clipboardEntry.To : clipboardEntry.From;
                        descriptor.To = swap ? clipboardEntry.From : clipboardEntry.To;
                        descriptor.Example = clipboardEntry.Example;
                        descriptor.Priority = clipboardEntry.Priority;

                        lvEntries.AddObject(descriptor);

                        _entries.Add(descriptor);
                    }
                }
            }
            else if (Clipboard.ContainsData(DataFormats.Text))
            {
                string text = (string)Clipboard.GetData(DataFormats.Text);

                if (!String.IsNullOrEmpty(text))
                {
                    string[] entries = text.Split(new[] {"\n", "\r\n"}, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string entry in entries)
                    {
                        string[] parts = entry.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);

                        if (parts.Length > 0)
                        {
                            descriptor = EditedEntryDescriptor.CreateNew();
                            descriptor.From = parts[0].Trim();
                            if (parts.Length > 1)
                            {
                                descriptor.To = parts[1].Trim();

                                if (parts.Length > 2)
                                {
                                    descriptor.Example = parts[2].Trim();
                                }
                            }

                            lvEntries.AddObject(descriptor);

                            _entries.Add(descriptor);
                        }
                    }
                }
            }

            if (descriptor != null)
            {
                lvEntries.SelectObject(descriptor);

                UpdateButtons();
            }
        }

        private void OnCutEntriesClick(object sender, EventArgs e)
        {
            CopySelectedEntries();
            RemoveSelectedEntries();
        }

        private void OnExcludeFromImport(object sender, EventArgs e)
        {
            ExcludeSelectedFromImport();
        }

        private void ExcludeSelectedFromImport()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(_root.ImportExcludes.Words.Value != null ? _root.ImportExcludes.Words.Value.TrimEnd(new []{' ', ';'}) : String.Empty);
            
            if (sb.Length > 0)
            {
                sb.Append("; ");
            }

            foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
            {
                sb.Append(descriptor.From);
                sb.Append("; ");
            }

            sb.Remove(sb.Length - 2, 2);

            _root.ImportExcludes.Words.Value = sb.ToString();
        }

        private void OnSelectAllClick(object sender, EventArgs e)
        {
            lvEntries.SelectAll();
        }

        private void OnEditSelected(object sender, EventArgs e)
        {
            lvEntries.CancelCellEdit();

            List<EditedEntryDescriptor> descriptors =  new List<EditedEntryDescriptor>();

            foreach (EditedEntryDescriptor descriptor in lvEntries.SelectedObjects)
            {
                descriptors.Add(descriptor);
            }

            AdvancedEntryEditorForm form = new AdvancedEntryEditorForm(_root, descriptors, _currentDictionary.Dictionary);

            _root.FormsManager.Add(form);
            form.PseudoParent = this;
            form.Show();

            form.Closed += delegate
            {
                if (form.NewEntriesAvailable)
                {
                    _entries.AddRange(form.GetNewEntries());

                    lvEntries.RefreshObjects(lvEntries.SelectedObjects);
                    lvEntries.AddObjects(form.GetNewEntries());
                }
                else
                {
                    lvEntries.RefreshObjects(lvEntries.SelectedObjects);   
                }

                UpdateButtons();

                _root.FormsManager.Remove(form);
            };
        }

        #region Import
        
        private class ImportThreadWorkerData : ThreadWorkerData
        {
            public ImportContext Context { get; set; }
            public Dictionary<string, Sentence> Result { get; set; }
        }

        private void OnImportClick(object sender, EventArgs e)
        {
            if (IsModified())
            {
                MessageBox.Show(Resources.MustSaveDict, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                return;
            }

            //

            if (Directory.Exists(_root.MachineSettings.LastBrowseDirectory))
            {
                ofdImportSubtitles.InitialDirectory = _root.MachineSettings.LastBrowseDirectory;
            }

            // select file to import

            if (ofdImportSubtitles.ShowDialog() == DialogResult.OK)
            {
                using (_root.MachineSettings.Edit())
                {
                    _root.MachineSettings.LastBrowseDirectory = Path.GetDirectoryName(ofdImportSubtitles.FileName);
                }

                // select dictionaries to exclude their words during import

                List<SelectedItem> items = new List<SelectedItem>();

                foreach (Dictionary dictionary in _dicts)
                {
                    items.Add(new SelectedItem(dictionary, true));
                }

                ListForm listForm = new ListForm(_root.MachineSettings);

                _root.FormsManager.Add(listForm);

                listForm.Text = Resources.SelectDicts;
                listForm.InfoText = Resources.SelectDictsToExclude;

                listForm.Items = items.ToArray();
                listForm.PseudoParent = this;
                listForm.Closed += delegate
                                       {
                                           if (listForm.DialogResult == DialogResult.OK)
                                           {
                                               ImportContext context = new ImportContext();

                                               context.ImportWordDelegate = CanImportWord;
                                               context.CodeEncoding = _root.UserSettings.ImportEncoding.Value;
                                               context.MinWordLen = _root.UserSettings.MinImportedWordLen.Value;
                                               context.ExcludedWords = new List<string>(_root.ImportExcludes.GetParsedWords());
                                               context.UseRegex = _root.UserSettings.UseRegExForExcludedWords.Value;
                                               context.ExcludedDicts = new List<Dictionary>();
                                               context.Files = ofdImportSubtitles.FileNames;

                                               foreach (SelectedItem item in listForm.Items)
                                               {
                                                   if (item.Selected)
                                                   {
                                                       context.ExcludedDicts.Add((Dictionary) item.Value);
                                                   }
                                               }

                                               ImportSubtitles(context);
                                           }

                                           _root.FormsManager.Remove(listForm);
                                       };

                // show form and import in close

                listForm.Show();
            }
        }

        private static bool CanImportWord(ImportContext context, string word)
        {
            if (String.IsNullOrEmpty(word))
            {
                return false;
            }

            if (word.Length < context.MinWordLen)
            {
                return false;
            }

            // search in excluded words

            foreach (string excludedWord in context.ExcludedWords)
            {
                if (context.UseRegex)
                {
                    if (Regex.IsMatch(word, excludedWord, RegexOptions.IgnoreCase))
                    {
                        return false;
                    }
                }
                else
                {
                    if (String.Compare(word, excludedWord, true) == 0)
                    {
                        return false;
                    }
                }
            }

            // search in excluded dictionaries

            foreach (Dictionary dictionary in context.ExcludedDicts)
            {
                foreach (KeyValuePair<int, Entry> item in dictionary.Entries.Items)
                {
                    if (String.Compare(item.Value.From, word, true) == 0)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        private void ImportSubtitles(ImportContext context)
        {
            ImportThreadWorkerData worker = new ImportThreadWorkerData { Context = context};

            BusyForm form = new BusyForm(worker);
            _root.FormsManager.Add(form);

            form.PseudoParent = this;
            form.WaitCaption = Resources.ImportingSubtitles;
            form.Show();

            form.Closed += delegate
            {
                if (!worker.Canceled)
                {
                    foreach (KeyValuePair<string, Sentence> word in worker.Result)
                    {
                        EditedEntryDescriptor descriptor = EditedEntryDescriptor.CreateNew();
                        descriptor.From = word.Key;
                        descriptor.Example = word.Value.Text;

                        _entries.Add(descriptor);
                    }

                    lvEntries.SetObjects(GetEditorContent());

                    UpdateButtons();
                }

                _root.FormsManager.Remove(form);
            };

            Thread thread = new Thread(ImportThreadWorker) { CurrentUICulture = Thread.CurrentThread.CurrentUICulture };
            thread.Start(worker);
        }

        private static void ImportThreadWorker(object obj)
        {
            ImportThreadWorkerData worker = obj as ImportThreadWorkerData;
            if (worker == null)
            {
                throw new InvalidOperationException();
            }

            SubtitlesImporter subtitlesImporter = new SubtitlesImporter(worker, worker.Context);
            subtitlesImporter.Perform();

            worker.Result = subtitlesImporter.Result;
            worker.Exit();
        }

        #endregion
    }
}
